#!/bin/dash

#Copyright (c) 2014 Luigi Tarenga <luigi.tarenga@gmail.com>
#Distributed under the terms of a MIT license.

export LANG=C
export sshopt="-o ControlPath=~/.ssh/lite-raft-socket-%h -o ControlMaster=auto \
-o ConnectTimeout=1 -o ServerAliveInterval=2 -o ServerAliveCountMax=3 \
-o PasswordAuthentication=no -o StrictHostKeyChecking=no -q"

cd ${0%/*}

if [ $# -ne 0 ] ; then
   echo "Usage: ${0##*/}"
   echo "  This is the main server of lite-raft. Usually it's started"
   echo "  with \"lite-raft start\" command."
   exit 0
fi

if [ -d /dev/shm ] ; then
   mkdir -p /dev/shm/lite-raft/temp
   ln -sf /dev/shm/lite-raft/temp temp
else
   mkdir -p temp
fi

quit () {
   pkill -f "ssh -MNf -o ControlPath=~/.ssh/lite-raft-socket-%h"
   rmdir temp/master_lockdir
   exit $1
}

trap 'quit 0' HUP INT TERM
if ! mkdir temp/master_lockdir 2> /dev/null ; then
   echo "cannot create lockdir. check if another lite-raft-server is running."
   exit 1
fi

mkdir -p temp/activity_lockdir_un

#hostname -s is not supported on HP-UX
hostname=$(hostname)
hostname=${hostname%%.*}
echo $hostname > temp/hostname
echo ""       > temp/current_leader
echo follower > temp/server_role
echo 0        > temp/commit_index
echo false    > temp/quorum_heartbeat
cp conf/election_timeout temp/election_timeout

if [ -f state-machine-snapshot/cur/last_included_index ] ; then
   cp state-machine-snapshot/cur/last_included_index temp/last_log_applied
else
   echo 0 > temp/last_log_applied
fi

echo "$(date) starting as follower."
while true ; do
   read cluster_nodes < conf/cluster_nodes
   for h in $cluster_nodes ; do
      if [ "$h" != "$hostname" ] ; then
         [ -S ~/.ssh/lite-raft-socket-$h ] || ssh -MNf $sshopt $h > /dev/null 2>&1
      fi
   done

   read server_role < temp/server_role
   sleep 1 & sleep_pid=$!
   case "$server_role" in 
    follower)
      lock_timeout=10 internals/activity-lock-ex internals/follower
    ;;
    candidate)
      #sleep between 0 and 0.999600 seconds. hex trick is more portable.
      delay=0.$((0x$(echo $(od -An -N1 -tx1 /dev/urandom))*3920))
      #where sleep doesn't support decimal fallback to perl sleep.
      sleep $delay 2> /dev/null || utils/sleep.pl $delay
      lock_timeout=10 internals/activity-lock-ex internals/candidate
    ;;
    leader)
      lock_timeout=10 internals/activity-lock-ex internals/leader
    ;;
   esac

   first_log_index=0
   read snapshot_period < conf/snapshot_period
   if [ -f state-machine-snapshot/cur/last_included_index ] ; then
      read first_log_index < state-machine-snapshot/cur/last_included_index
   fi
   read last_log_index < state/last_log_index
   if [ $((last_log_index-first_log_index)) -gt $snapshot_period ] ; then
      lock_timeout=10 internals/activity-lock-ex internals/create-snapshot
   fi

   wait $sleep_pid
done
